using System;
using System.Collections;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using RedGate.SQLDataGenerator.Engine.Generators;
using RedGate.SQLDataGenerator.Engine.Generators.Static;
using RedGate.SQLDataGenerator.Engine.Generators.Support;

namespace IPAddress
{
    [Generator(typeof(string), "Business", "IPv4 Address", "Generates a random IPv4 Address")]
    public class IPv4AddressGenerator : GeneratorBase, IGeneratorUIStyle, IGeneratorUI
    {
        private bool m_IncludeRemoteClassAAddresses = true;
        private bool m_IncludeRemoteClassBAddresses = true;
        private bool m_IncludeRemoteClassCAddresses = true;
        private bool m_LocalAddresses = true;
        private bool m_IncludeLoopbackAddresses = true;
        private bool m_IncludeMulticastAddresses = true;
        private UserControl m_UI;

        public bool IncludeRemoteClassAAddresses
        {
            get { return m_IncludeRemoteClassAAddresses; }
            set { m_IncludeRemoteClassAAddresses = value; }
        }

        public bool IncludeRemoteClassBAddresses
        {
            get { return m_IncludeRemoteClassBAddresses; }
            set { m_IncludeRemoteClassBAddresses = value; }
        }

        public bool IncludeRemoteClassCAddresses
        {
            get { return m_IncludeRemoteClassCAddresses; }
            set { m_IncludeRemoteClassCAddresses = value; }
        }
        
        public bool LocalAddresses
        {
            get { return m_LocalAddresses; }
            set { m_LocalAddresses = value; }
        }

        public bool IncludeLoopbackAddresses
        {
            get { return m_IncludeLoopbackAddresses; }
            set { m_IncludeLoopbackAddresses = value; }
        }

        public bool IncludeMulticastAddresses
        {
            get { return m_IncludeMulticastAddresses; }
            set { m_IncludeMulticastAddresses = value; }
        }

        public IPv4AddressGenerator(GeneratorParameters parameters) : base(parameters)
        {

        }

        public UIGeneratorStyle UIGeneratorStyle
        {
            get { return UIGeneratorStyle.UIPartial; }
        }

        #region IGeneratorUI Members
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public UserControl UserControl
        {
            get
            {
                if (m_UI == null)
                    m_UI = new IPv4AddressGeneratorUI(this, m_Parameters);
                return m_UI;
            }
            set
            {
                m_UI = null;
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IGeneratorSimpleUI IGeneratorSimpleUI
        {
            get { return UserControl as IGeneratorSimpleUI; }
            set { m_UI = null; }
        }
        #endregion

        public override IEnumerator GetNonNullEnumerator(GenerationSession session)
        {
            RandomRange range = new RandomRange();
            
            if(m_IncludeRemoteClassAAddresses)
            {
                if (m_LocalAddresses)
                {
                    range.Include(ParseString("10.0.0.0"), ParseString("10.255.255.255"));
                }
                else
                {
                    range.Include(ParseString("1.0.0.0"), ParseString("9.255.255.255"));
                    range.Include(ParseString("11.0.0.0"), ParseString("126.255.255.255"));
                }
            }

            if(m_IncludeLoopbackAddresses)
            {
                range.Include(ParseString("127.0.0.0"), ParseString("127.255.255.255"));                
            }

            if(m_IncludeRemoteClassBAddresses)
            {
                if (m_LocalAddresses)
                {
                    range.Include(ParseString("172.16.0.0"), ParseString("172.31.255.255"));
                }
                else
                {
                    range.Include(ParseString("128.0.0.0"), ParseString("172.15.255.255"));
                    range.Include(ParseString("172.32.0.0"), ParseString("191.255.255.255"));
                }
            }

            if(m_IncludeRemoteClassCAddresses)
            {
                if (m_LocalAddresses)
                {
                    range.Include(ParseString("192.168.0.0"), ParseString("192.168.255.255"));
                }
                else
                {
                    range.Include(ParseString("192.0.0.0"), ParseString("192.167.255.255"));
                    range.Include(ParseString("192.169.0.0"), ParseString("223.255.255.255"));
                }
            }

            if (m_IncludeMulticastAddresses && !m_LocalAddresses)
            {
                range.Include(ParseString("224.0.0.0"), ParseString("224.255.255.255"));                
            }


            IEnumerator e = range.GetEnumerator(m_Seed);

            while(e.MoveNext())
            {
                yield return GetString((int) e.Current);
            }
        }

        public string GetString(int ip)
        {
            StringBuilder sb = new StringBuilder(15);

            sb.Append(((ip >> 24) & 0xff).ToString());
            sb.Append(".");
            sb.Append(((ip >> 16) & 0xff).ToString());
            sb.Append(".");
            sb.Append(((ip >> 8) & 0xff).ToString());
            sb.Append(".");
            sb.Append((ip & 0xff).ToString());

            return sb.ToString();
        }

        public int ParseString(string str)
        {
            string[] parts = str.Split('.');

            if(parts.Length != 4)
            {
                throw new ArgumentException("Ip address must be 4 bytes separated by .");
            }

            try
            {
                byte a = byte.Parse(parts[0]);
                byte b = byte.Parse(parts[1]);
                byte c = byte.Parse(parts[2]);
                byte d = byte.Parse(parts[3]);

                return a << 24 | b << 16 | c << 8 | d;
            }
            catch
            {
                throw new ArgumentException("Ip address must be 4 bytes separated by .");
            }
        }
    }
}